Skip to content

Conversation

@rkritika1508
Copy link
Collaborator

@rkritika1508 rkritika1508 commented Feb 9, 2026

Summary

Target issue is #28.
Explain the motivation for making this change. What existing problem does the pull request solve?
We don't want to populate the validator_log each time a validator has parsed a message. There will be a flag include_all_validator_logs which if disabled will populate only failed validator logs. Otherwise, we will populate all logs to the validator_log table.

Checklist

Before submitting a pull request, please ensure that you mark these task.

  • Ran fastapi run --reload app/main.py or docker compose up in the repository root and test.
  • If you've fixed a bug or added code that is tested and has test cases.

Notes

Please add here if any other information is required for the reviewer.

Summary by CodeRabbit

  • New Features

    • Validator configuration API: create, list, view, update, and delete validator rules with stage/type/on-fail settings.
    • Optional verbose validator logging: persist all validation results (including successful checks) when enabled.
  • Database

    • Added storage for validator configurations with indexing and uniqueness to support the new API.
  • Tests

    • Added unit and integration tests for validator config CRUD; minor test expectation adjustments for guardrails.

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

Adds validator configuration CRUD, DB model and migrations, test support for validator configs, and a flag to include successful validator logs in persisted guardrails logs; plus related refactors, import path adjustments, and test/CI env tweaks.

Changes

Cohort / File(s) Summary
Guardrails logging & API
backend/app/api/routes/guardrails.py
Added include_all_validator_logs: bool = False param to run_guardrails, _validate_with_guard, and add_validator_logs; imported PassResult and filter logic to optionally persist passing validator results.
Validator Config API & Router
backend/app/api/routes/validator_configs.py, backend/app/api/main.py
New FastAPI router validator_configs with CRUD endpoints; router registered in main.
DB model & migrations
backend/app/models/config/validator_config.py, backend/app/alembic/versions/003_added_validator_config.py, .../001_*.py, .../002_*.py
New ValidatorConfig SQLModel and migration (003); minor typing/style adjustments to prior alembic revision metadata in 001/002.
CRUD & core logic
backend/app/crud/validator_config.py, backend/app/core/constants.py, backend/app/core/enum.py, backend/app/core/guardrail_controller.py, backend/app/core/validators/config/*
New ValidatorConfigCrud with create/list/get/update/delete/flatten; added VALIDATOR_CONFIG_SYSTEM_FIELDS, new enums Stage and ValidatorType, and import path adjustments for validator config classes.
Schemas & utils
backend/app/schemas/validator_config.py, backend/app/schemas/guardrail_config.py, backend/app/utils.py
New Pydantic/SQLModel schemas for validator config (create/update/response); updated imports and added split_validator_payload to partition model vs config fields.
Validator log persistence & imports
backend/app/crud/validator_log.py, backend/app/models/__init__.py, backend/app/crud/request_log.py
Fixed import paths for logging models (request_log, validator_log) and propagated changes to imports.
Tests — unit & integration
backend/app/tests/test_validator_configs.py, backend/app/tests/test_validator_configs_integration.py, backend/app/tests/conftest.py, backend/app/tests/test_guardrails_api_integration.py, backend/app/tests/utils/constants.py
Added extensive unit and integration tests for validator configs; test DB setup/teardown fixtures; updated guardrails integration expectations and test API path constant.
Env / examples
.env.test (deleted), .env.test.example
Removed .env.test; updated .env.test.example Postgres DB name.
Misc formatting / exports
backend/app/crud/__init__.py, backend/app/models/__init__.py, backend/app/schemas/guardrail_config.py
Minor formatting and import-path adjustments across modules.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant API as Guardrails API
  participant ValidatorRouter as ValidatorConfigs Router
  participant CRUD as ValidatorConfig CRUD
  participant DB as Database
  participant LogStore as ValidatorLog persistence

  Client->>API: POST /guardrails/validators/configs (create)
  API->>ValidatorRouter: route handler
  ValidatorRouter->>CRUD: create(payload)
  CRUD->>DB: INSERT validator_config
  DB-->>CRUD: OK (row)
  CRUD-->>ValidatorRouter: flattened response
  ValidatorRouter-->>API: 201 Created
  API-->>Client: response

  Client->>API: POST /guardrails/validate (run_guardrails, include_all_validator_logs?)
  API->>API: _validate_with_guard(..., include_all_validator_logs)
  API->>LogStore: add_validator_logs(iter_logs, include_all_validator_logs)
  alt include_all_validator_logs == false
    LogStore-->>DB: persist only failing validator logs
  else
    LogStore-->>DB: persist all validator logs (including PassResult)
  end
  LogStore-->>API: ack
  API-->>Client: validation result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nishika26
  • dennyabrain
  • AkhileshNegi

Poem

🐰 A little flag that hops and stays,
It keeps each pass or hides the praise.
New configs sprout, migrations hum,
Tests hop in—now look how they run.
The rabbit stamps, "Logs — chosen!" 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title mentions a 'custom flag for validator log table' which directly relates to the main feature of adding the include_all_validator_logs parameter throughout the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/validator-log-flag

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/app/tests/test_guardrails_api_integration.py (1)

1-196: 🛠️ Refactor suggestion | 🟠 Major

No tests for the new include_all_validator_logs feature.

The main feature of this PR — the include_all_validator_logs flag — has no test coverage. Consider adding at least:

  1. A test with include_all_validator_logs=True that verifies all validator logs (including passes) are persisted.
  2. A test with the default (False) that verifies only failure logs are persisted.

This would confirm the filtering logic works end-to-end.

🤖 Fix all issues with AI agents
In `@backend/app/api/routes/guardrails.py`:
- Around line 180-181: Replace the explicit equality comparison to False in the
guardrails check with a boolean negation: in the if statement that currently
reads "if include_all_validator_logs == False and isinstance(result,
PassResult):", use "if not include_all_validator_logs and isinstance(result,
PassResult):" so the code uses idiomatic Python and avoids issues when
include_all_validator_logs is a truthy/falsy non-bool; update the condition
where include_all_validator_logs and PassResult are checked.
🧹 Nitpick comments (2)
backend/app/api/routes/guardrails.py (2)

24-28: include_all_validator_logs is a query parameter on a POST endpoint — consider moving it into the request body.

Since payload is a Pydantic model parsed from the JSON body, FastAPI will treat include_all_validator_logs as a query parameter (e.g., ?include_all_validator_logs=true). Mixing body and query params on a POST can be surprising to API consumers. Consider adding this field to GuardrailRequest instead for a cleaner contract.


163-163: Long signature — consider grouping parameters or wrapping across lines for readability.

This is a minor readability nit. The function signature is quite long on a single line.

Suggested formatting
-def add_validator_logs(guard: Guard, request_log_id: UUID, validator_log_crud: ValidatorLogCrud, include_all_validator_logs: bool = False):
+def add_validator_logs(
+    guard: Guard,
+    request_log_id: UUID,
+    validator_log_crud: ValidatorLogCrud,
+    include_all_validator_logs: bool = False,
+):

@rkritika1508 rkritika1508 changed the title Validator log table in guardrails API Custom flag for validator log table Feb 9, 2026
@rkritika1508 rkritika1508 added enhancement New feature or request ready-for-review labels Feb 9, 2026
@rkritika1508 rkritika1508 enabled auto-merge (squash) February 9, 2026 10:27
@rkritika1508 rkritika1508 self-assigned this Feb 9, 2026
@rkritika1508 rkritika1508 linked an issue Feb 9, 2026 that may be closed by this pull request
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@backend/app/api/routes/validator_configs.py`:
- Around line 23-28: The create_validator endpoint accepts organization_id and
project_id as unauthenticated query params—fix by enforcing tenant validation:
extract the authenticated tenant identifiers from the AuthDep (e.g., user or
token fields on the AuthDep dependency used by create_validator), compare them
to the incoming organization_id and project_id, and reject the request (raise an
appropriate HTTP 403/Forbidden) if they do not match; alternatively, remove
those query params and derive organization_id/project_id from the AuthDep
principal and pass only validated IDs into the CRUD layer so ValidatorCreate
handling and any calls that use session, create_validator, and the downstream
validator CRUD functions cannot operate across tenants.

In `@backend/app/schemas/validator_config.py`:
- Around line 24-30: ValidatorUpdate currently sets model_config =
ConfigDict(extra="forbid"), which blocks any arbitrary config keys in PATCH
payloads and prevents them reaching split_validator_payload/CRUD update; change
ValidatorUpdate to use model_config = ConfigDict(extra="allow") (matching
ValidatorCreate/ValidatorBase) so config fields in the incoming update are
accepted and can be merged by the existing split_validator_payload and the CRUD
update method.
- Around line 33-34: ValidatorResponse currently inherits only fields from
ValidatorBase and relies on extra="allow", so add explicit response fields to
the ValidatorResponse model to make them visible in OpenAPI: declare id: UUID4,
organization_id: UUID4, project_id: UUID4, created_at: datetime, and updated_at:
datetime (import UUID4 from pydantic and datetime from datetime) as attributes
on the ValidatorResponse class while keeping the existing inheritance from
ValidatorBase so the schema includes both base validator properties and these
persistent metadata fields.

In `@backend/app/tests/conftest.py`:
- Around line 13-17: When running tests the DB name can be empty and cause
destructive truncation of the wrong DB; add a validation in config.py (where
Settings or environment loading occurs) that checks if ENVIRONMENT == "testing"
and POSTGRES_DB is falsy/empty and, if so, raise a clear RuntimeError/ValueError
explaining that .env.test must be created and POSTGRES_DB must be set; also
ensure the error surfaces before create_engine/ test setup (reference
settings.SQLALCHEMY_DATABASE_URI and the POSTGRES_DB/ENVIRONMENT settings names)
and add a short comment/docs note telling devs to copy .env.test.example to
.env.test before running tests.

In `@backend/app/tests/test_validator_configs_integration.py`:
- Around line 60-82: The update_validator helper constructs its URL without the
trailing slash before DEFAULT_QUERY_PARAMS while get_validator and
delete_validator include it; update the URL construction in update_validator to
include the "/" between validator_id and DEFAULT_QUERY_PARAMS (same pattern as
get_validator/delete_validator) so the path becomes BASE_URL + validator_id +
"/" + DEFAULT_QUERY_PARAMS, ensuring consistent URL formatting across
create_validator/get_validator/update_validator/delete_validator and using the
same BASE_URL and DEFAULT_QUERY_PARAMS symbols.

In `@backend/app/utils.py`:
- Around line 26-28: The overlap check computing overlap = set(model_fields) &
set(config_fields) and raising ValueError is dead code because earlier routing
guarantees mutual exclusivity; remove the overlap calculation and the
conditional that raises the ValueError (or replace it with the intended
validation if you meant to check an external source), and ensure any intended
shadowing validation targets the correct input (e.g., compare
external_config_keys against model_fields instead of config_fields) so only
meaningful checks remain; search for model_fields, config_fields, and overlap to
locate and update the code.
🧹 Nitpick comments (12)
.env.test.example (1)

13-13: LGTM! Good improvements to the test database name.

The change from hyphens to underscores and the addition of the _testing suffix are both good practices:

  • Underscores avoid potential quoting issues with database identifiers
  • The _testing suffix clearly distinguishes this from production databases

Optional nitpick: The static analysis tool suggests moving POSTGRES_DB before POSTGRES_SERVER for alphabetical ordering. This is purely cosmetic and has no functional impact.

📝 Optional reordering for consistency
 # Postgres
+POSTGRES_DB=kaapi_guardrails_testing
+POSTGRES_PORT=5432
+POSTGRES_PASSWORD=postgres
 POSTGRES_SERVER=localhost
-POSTGRES_DB=kaapi_guardrails_testing
-POSTGRES_PORT=5432
 POSTGRES_USER=postgres
-POSTGRES_PASSWORD=postgres
backend/app/alembic/versions/002_added_validator_log.py (1)

8-8: Unused imports: Sequence and Union are no longer referenced.

Since the type annotations were removed from down_revision, branch_labels, and depends_on, these imports are dead code.

Proposed fix
-from typing import Sequence, Union
backend/app/api/routes/guardrails.py (2)

24-28: Consider whether include_all_validator_logs should be a query parameter on the POST endpoint.

Currently this flag is a bare query parameter on the POST /guardrails/ endpoint. If this is intended as a per-request configuration, it might be cleaner to include it in the GuardrailRequest body model instead, keeping all request-level config in one place.

If it's meant as an operational/debug toggle, the query parameter approach is fine — just flagging for intentional design review.


13-13: Nit: extra whitespace in import.

Line 13 has a double space before RequestLogUpdate.

Proposed fix
-from app.models.logging.request_log import  RequestLogUpdate, RequestStatus
+from app.models.logging.request_log import RequestLogUpdate, RequestStatus
backend/app/crud/validator_log.py (1)

1-1: Unused imports: UUID and uuid4 are not referenced in this file.

The create method receives a pre-constructed ValidatorLog, so neither UUID nor uuid4 is needed here.

Proposed fix
-from uuid import UUID, uuid4
backend/app/models/config/validator_config.py (1)

7-8: Redundant duplicate import of Field.

Field is imported twice — once as SQLField (Line 7) and once as Field (Line 8). They are the same symbol. Consider using just one import with the alias where needed, or simply use Field throughout.

♻️ Suggested cleanup
-from sqlmodel import Field as SQLField
-from sqlmodel import SQLModel, Field
+from sqlmodel import SQLModel, Field

Then on Line 50, replace SQLField( with Field(.

backend/app/schemas/validator_config.py (1)

1-3: Unused imports.

datetime (Line 1) and UUID (Line 3) are imported but not used in the current schema definitions. They would become used if ValidatorResponse is updated per the suggestion above.

backend/app/crud/validator_config.py (2)

59-63: Use is not None instead of truthiness check for optional enum filters.

if stage: and if type: rely on truthiness. While enum members are truthy, using is not None is more explicit and defensive against future enum changes or unexpected falsy values.

♻️ Suggested fix
-        if stage:
+        if stage is not None:
             query = query.where(ValidatorConfig.stage == stage)
 
-        if type:
+        if type is not None:
             query = query.where(ValidatorConfig.type == type)

105-107: Bare except Exception in update swallows context.

The generic except Exception block on Lines 105–107 catches everything, rolls back, and re-raises. This is fine for ensuring rollback, but consider logging the exception before re-raising for observability, especially since this is a catch-all beyond IntegrityError.

backend/app/api/routes/validator_configs.py (2)

19-31: POST endpoint should return HTTP 201 for resource creation.

The create endpoint defaults to HTTP 200. Convention for resource creation is 201 Created.

♻️ Suggested fix
 `@router.post`(
         "/",
-        response_model=APIResponse[ValidatorResponse]
+        response_model=APIResponse[ValidatorResponse],
+        status_code=201,
     )

81-94: DELETE returns 200 with a message body — consider 204 No Content.

REST convention for a successful delete is typically HTTP 204 with no body. However, returning 200 with a message is also acceptable and may be preferred for consistency with the APIResponse wrapper pattern used throughout.

backend/app/tests/test_validator_configs.py (1)

79-90: Catch HTTPException instead of bare Exception.

The CRUD layer raises HTTPException(404, "Validator not found"). Catching generic Exception weakens the test — it would pass even if the code raised a different, unexpected exception type.

Proposed fix
+from fastapi import HTTPException
+
 ...
 
     def test_not_found(self, mock_session):
         mock_session.get.return_value = None
 
-        with pytest.raises(Exception) as exc:
+        with pytest.raises(HTTPException) as exc:
             validator_config_crud.get(
                 mock_session,
                 TEST_VALIDATOR_ID,
                 TEST_ORGANIZATION_ID,
                 TEST_PROJECT_ID,
             )
 
-        assert "Validator not found" in str(exc.value)
+        assert exc.value.status_code == 404
+        assert "Validator not found" in exc.value.detail

Comment on lines +23 to +28
def create_validator(
payload: ValidatorCreate,
session: SessionDep,
organization_id: int,
project_id: int,
_: AuthDep,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's understand the function context and parameter types
cat -n backend/app/api/routes/validator_configs.py | head -50

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 1753


🏁 Script executed:

# Search for how organization_id and project_id are handled in the codebase
rg -n "organization_id|project_id" --type=py -C3 backend/app/api/ | head -200

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 4736


🏁 Script executed:

# Check the auth validation patterns - look for how AuthDep validates tenant access
rg -n "AuthDep|SessionDep" --type=py -C2 backend/app/api/routes/ | head -150

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 4112


🏁 Script executed:

# Check if there's middleware or dependency validation for these parameters
fd -e py backend/app | xargs rg -l "organization_id.*validate|verify.*organization" --type=py

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 429


🏁 Script executed:

# Look for the ValidatorCreate schema to understand parameter definitions
rg -n "class ValidatorCreate|ValidatorCreate" --type=py -C5 backend/

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 3845


🏁 Script executed:

# Find and examine the AuthDep implementation
rg -n "AuthDep" backend/app/api/deps.py -A 10

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 136


🏁 Script executed:

# Check the full deps.py file to understand dependency injection
cat -n backend/app/api/deps.py

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 1438


🏁 Script executed:

# Examine CRUD methods for validation
cat -n backend/app/crud/validator_config.py | head -80

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 2872


🏁 Script executed:

# Check if session is tenant-aware
rg -n "Session|session" backend/app/crud/validator_config.py -B 2 -A 5 | head -100

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 3018


organization_id and project_id are unauthenticated query parameters with no tenant validation.

These tenant-scoping parameters are passed as plain query parameters with zero validation against the authenticated user's identity. The AuthDep dependency only validates a static bearer token—it does not extract or enforce tenant affiliation. Any authenticated caller can pass arbitrary organization_id and project_id values to create, read, update, or delete validators in any tenant. The CRUD layer accepts these parameters without access control checks, enabling cross-tenant data access.

🤖 Prompt for AI Agents
In `@backend/app/api/routes/validator_configs.py` around lines 23 - 28, The
create_validator endpoint accepts organization_id and project_id as
unauthenticated query params—fix by enforcing tenant validation: extract the
authenticated tenant identifiers from the AuthDep (e.g., user or token fields on
the AuthDep dependency used by create_validator), compare them to the incoming
organization_id and project_id, and reject the request (raise an appropriate
HTTP 403/Forbidden) if they do not match; alternatively, remove those query
params and derive organization_id/project_id from the AuthDep principal and pass
only validated IDs into the CRUD layer so ValidatorCreate handling and any calls
that use session, create_validator, and the downstream validator CRUD functions
cannot operate across tenants.

Comment on lines +24 to +30
class ValidatorUpdate(SQLModel):
model_config = ConfigDict(extra="forbid")

type: Optional[ValidatorType] = None
stage: Optional[Stage] = None
on_fail_action: Optional[GuardrailOnFail] = None
is_enabled: Optional[bool] = None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

extra="forbid" on ValidatorUpdate prevents config field updates.

ValidatorCreate (via ValidatorBase) uses extra="allow", enabling callers to pass arbitrary config keys that get split into the JSONB config column. However, ValidatorUpdate uses extra="forbid", which means any config key in a PATCH payload will be rejected by Pydantic before it reaches the CRUD layer's split_validator_payload. This effectively makes config fields immutable after creation.

If config updates are intended (the CRUD update method already handles merging config fields), change this to extra="allow":

🐛 Proposed fix
 class ValidatorUpdate(SQLModel):
-    model_config = ConfigDict(extra="forbid")
+    model_config = ConfigDict(extra="allow")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class ValidatorUpdate(SQLModel):
model_config = ConfigDict(extra="forbid")
type: Optional[ValidatorType] = None
stage: Optional[Stage] = None
on_fail_action: Optional[GuardrailOnFail] = None
is_enabled: Optional[bool] = None
class ValidatorUpdate(SQLModel):
model_config = ConfigDict(extra="allow")
type: Optional[ValidatorType] = None
stage: Optional[Stage] = None
on_fail_action: Optional[GuardrailOnFail] = None
is_enabled: Optional[bool] = None
🤖 Prompt for AI Agents
In `@backend/app/schemas/validator_config.py` around lines 24 - 30,
ValidatorUpdate currently sets model_config = ConfigDict(extra="forbid"), which
blocks any arbitrary config keys in PATCH payloads and prevents them reaching
split_validator_payload/CRUD update; change ValidatorUpdate to use model_config
= ConfigDict(extra="allow") (matching ValidatorCreate/ValidatorBase) so config
fields in the incoming update are accepted and can be merged by the existing
split_validator_payload and the CRUD update method.

Comment on lines +33 to +34
class ValidatorResponse(ValidatorBase):
pass
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

ValidatorResponse lacks explicit fields for id, timestamps, etc.

ValidatorResponse inherits only type, stage, on_fail_action, is_enabled from ValidatorBase. Fields like id, organization_id, project_id, created_at, and updated_at are not declared — they only survive because extra="allow" is inherited. This makes the response schema undocumented in OpenAPI and fragile.

Consider adding the key response fields explicitly so they appear in the generated API docs:

♻️ Suggested improvement
 class ValidatorResponse(ValidatorBase):
-    pass
+    id: UUID
+    organization_id: int
+    project_id: int
+    created_at: datetime
+    updated_at: datetime
🤖 Prompt for AI Agents
In `@backend/app/schemas/validator_config.py` around lines 33 - 34,
ValidatorResponse currently inherits only fields from ValidatorBase and relies
on extra="allow", so add explicit response fields to the ValidatorResponse model
to make them visible in OpenAPI: declare id: UUID4, organization_id: UUID4,
project_id: UUID4, created_at: datetime, and updated_at: datetime (import UUID4
from pydantic and datetime from datetime) as attributes on the ValidatorResponse
class while keeping the existing inheritance from ValidatorBase so the schema
includes both base validator properties and these persistent metadata fields.

Comment on lines +13 to +17
test_engine = create_engine(
str(settings.SQLALCHEMY_DATABASE_URI),
echo=False,
pool_pre_ping=True,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "ENVIRONMENT|POSTGRES_DB|testing|SQLALCHEMY_DATABASE_URI" --type=py -C3 backend/app/core/config.py

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 1893


🏁 Script executed:

# Check if .env.test exists and show its content
find . -name ".env.test" -o -name ".env.test.*" | head -20

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 97


🏁 Script executed:

# Also check if there's validation for POSTGRES_DB being set
rg -n "POSTGRES_DB|model_validate|validate_" --type=py backend/app/core/config.py -A2 -B2

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 422


🏁 Script executed:

# Check .env.test.example content
cat -n ./.env.test.example

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 999


🏁 Script executed:

# Check Settings class definition and env_file loading behavior
rg -n "class Settings|env_file|ConfigFile" --type=py backend/app/core/config.py -A5 -B2 | head -50

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 756


Test database isolation is not validated; missing .env.test risks data loss.

The test engine uses POSTGRES_DB from environment configuration, which defaults to an empty string. While .env.test.example correctly specifies POSTGRES_DB=kaapi_guardrails_testing, the actual .env.test file does not exist in the repository. If developers fail to create .env.test from the example, POSTGRES_DB remains empty, causing the database URI to connect to PostgreSQL's default or current schema instead of an isolated test database. The clean_db fixture would then truncate tables in the wrong database.

Add validation in config.py to raise an error if POSTGRES_DB is empty when ENVIRONMENT=testing, and document that .env.test must be created before running tests.

🤖 Prompt for AI Agents
In `@backend/app/tests/conftest.py` around lines 13 - 17, When running tests the
DB name can be empty and cause destructive truncation of the wrong DB; add a
validation in config.py (where Settings or environment loading occurs) that
checks if ENVIRONMENT == "testing" and POSTGRES_DB is falsy/empty and, if so,
raise a clear RuntimeError/ValueError explaining that .env.test must be created
and POSTGRES_DB must be set; also ensure the error surfaces before
create_engine/ test setup (reference settings.SQLALCHEMY_DATABASE_URI and the
POSTGRES_DB/ENVIRONMENT settings names) and add a short comment/docs note
telling devs to copy .env.test.example to .env.test before running tests.

Comment on lines +60 to +82
def create_validator(self, client, payload_key="minimal", **kwargs):
"""Helper to create a validator."""
payload = {**VALIDATOR_PAYLOADS[payload_key], **kwargs}
return client.post(f"{BASE_URL}{DEFAULT_QUERY_PARAMS}", json=payload)

def get_validator(self, client, validator_id):
"""Helper to get a specific validator."""
return client.get(f"{BASE_URL}{validator_id}/{DEFAULT_QUERY_PARAMS}")

def list_validators(self, client, **query_params):
"""Helper to list validators with optional filters."""
params_str = f"?organization_id={TEST_ORGANIZATION_ID}&project_id={TEST_PROJECT_ID}"
if query_params:
params_str += "&" + "&".join(f"{k}={v}" for k, v in query_params.items())
return client.get(f"{BASE_URL}{params_str}")

def update_validator(self, client, validator_id, payload):
"""Helper to update a validator."""
return client.patch(f"{BASE_URL}{validator_id}{DEFAULT_QUERY_PARAMS}", json=payload)

def delete_validator(self, client, validator_id):
"""Helper to delete a validator."""
return client.delete(f"{BASE_URL}{validator_id}/{DEFAULT_QUERY_PARAMS}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the route file and examine route definitions
find backend -name "*validator*" -type f | head -20

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 946


🏁 Script executed:

# Look for BASE_URL and DEFAULT_QUERY_PARAMS definitions in the test file
rg -n "BASE_URL|DEFAULT_QUERY_PARAMS" backend/app/tests/test_validator_configs_integration.py

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 901


🏁 Script executed:

# Examine the route definitions for validator configs
rg -n "@app|@router" backend/app/api/routes/ -A 5 | grep -i validator

Repository: ProjectTech4DevAI/kaapi-guardrails

Length of output: 2407


Inconsistent trailing slash in URL construction across helpers.

update_validator (line 78) omits the trailing / before query params, while get_validator (line 67) and delete_validator (line 82) include it:

  • Line 67: f"{BASE_URL}{validator_id}/{DEFAULT_QUERY_PARAMS}"/api/v1/.../configs/<id>/?org=...
  • Line 78: f"{BASE_URL}{validator_id}{DEFAULT_QUERY_PARAMS}"/api/v1/.../configs/<id>?org=...
  • Line 82: f"{BASE_URL}{validator_id}/{DEFAULT_QUERY_PARAMS}"/api/v1/.../configs/<id>/?org=...

This inconsistency should be fixed by adding the / before query params in update_validator:

Proposed fix
     def update_validator(self, client, validator_id, payload):
         """Helper to update a validator."""
-        return client.patch(f"{BASE_URL}{validator_id}{DEFAULT_QUERY_PARAMS}", json=payload)
+        return client.patch(f"{BASE_URL}{validator_id}/{DEFAULT_QUERY_PARAMS}", json=payload)
🤖 Prompt for AI Agents
In `@backend/app/tests/test_validator_configs_integration.py` around lines 60 -
82, The update_validator helper constructs its URL without the trailing slash
before DEFAULT_QUERY_PARAMS while get_validator and delete_validator include it;
update the URL construction in update_validator to include the "/" between
validator_id and DEFAULT_QUERY_PARAMS (same pattern as
get_validator/delete_validator) so the path becomes BASE_URL + validator_id +
"/" + DEFAULT_QUERY_PARAMS, ensuring consistent URL formatting across
create_validator/get_validator/update_validator/delete_validator and using the
same BASE_URL and DEFAULT_QUERY_PARAMS symbols.

Comment on lines +26 to +28
overlap = set(model_fields) & set(config_fields)
if overlap:
raise ValueError(f"Config keys conflict with reserved field names: {overlap}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Overlap check is unreachable — the if/else on lines 21–24 guarantees mutual exclusivity.

Every key is routed to exactly one of model_fields or config_fields, so set(model_fields) & set(config_fields) is always empty and this ValueError can never be raised.

If the intent is to validate something else (e.g., config keys that shadow system field names from an external source), this needs a different approach. Otherwise, consider removing the dead check to avoid confusion.

🤖 Prompt for AI Agents
In `@backend/app/utils.py` around lines 26 - 28, The overlap check computing
overlap = set(model_fields) & set(config_fields) and raising ValueError is dead
code because earlier routing guarantees mutual exclusivity; remove the overlap
calculation and the conditional that raises the ValueError (or replace it with
the intended validation if you meant to check an external source), and ensure
any intended shadowing validation targets the correct input (e.g., compare
external_config_keys against model_fields instead of config_fields) so only
meaningful checks remain; search for model_fields, config_fields, and overlap to
locate and update the code.

auto-merge was automatically disabled February 10, 2026 07:00

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ready-for-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validator log table updates

1 participant